import importlib
import json
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, Concatenate, ParamSpec, Self, TypeVar

from zkl_pyutils_fsspec import Fs, FsLike, resolve_fs
from zkl_pyutils_serialization import parse_json_value

P = ParamSpec('P')
T = TypeVar('T')


@dataclass(kw_only=True)
class RecordJson:
    module: str
    qualname: str
    args: tuple[Any, ...] | None = None
    kwargs: dict[str, Any] | None = None

    @classmethod
    def of(cls, loader: Callable[Concatenate[Fs, P], T] | type[T], *args: P.args, **kwargs: P.kwargs) -> Self:
        return cls(
            module=loader.__module__,
            qualname=loader.__qualname__,
            args=args, kwargs=kwargs)

    def load(self, fs: Fs, *args: P.args, **kwargs: P.kwargs) -> T:
        obj = importlib.import_module(self.module)
        for name in self.qualname.split('.'):
            obj = getattr(obj, name)
        args = args if self.args is None else (*self.args, *args)
        kwargs = kwargs if self.kwargs is None else {**self.kwargs, **kwargs}
        # noinspection PyCallingNonCallable
        return obj(fs, *args, **kwargs)


record_json_file_name: str = 'record.json'


def load_record(fs: FsLike, *args, **kwargs):
    fs = resolve_fs(fs)
    with fs.open(record_json_file_name, 'rt', encoding='utf-8') as io:
        record_json = parse_json_value(json.load(io), RecordJson)
    return record_json.load(fs, *args, **kwargs)
